using System;
using System.Drawing;
using System.Linq;

using SPR.Classes;

using Styx;
using Styx.Combat.CombatRoutine;
using Styx.Helpers;
using Styx.Logic;
using Styx.Logic.Combat;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;

using TreeSharp;

// Credits
//-------------
// * cowdude for his initial work with Felmaster and giving me inspiration to create this CC.
// * All the Singular Developers For there tiresome work and some supporting code
// * bobby53 For his Disclaimer (Couldn't have said it better)
// * Kickazz006 for his BlankProfile.xml
// * Shaddar & Mentally for thier initial detection code of HourofTwilight, FadingLight and Shrapnel
// * bennyquest for his continued efforts in reporting issues!
// * Jamjar0207 for his Protection Warrior Rotation and Fire Mage
// * gniegsch for his Arms Warrior Rotations

namespace SPR
{
    using global::SPR.GUI;
    using global::SPR.Helpers;

    public class SPR : CombatRoutine
    {
        private static readonly SPR CLUInstance = new SPR();

        /// <summary>
        /// An instance of the SPR class
        /// </summary>
        public static SPR Instance { get { return CLUInstance; } }

        public static readonly Version Version = new Version(2, 2, 9);

        public override string Name { get { return "SPR (Codified Likeness Utility) " + Version; } }

        public override WoWClass Class { get { return StyxWoW.Me.Class; } }

        private static LocalPlayer Me { get { return ObjectManager.Me; } }

        /// <summary>
        /// GUI Setting for Automaticly handling Cooldowns (On=True/Off=False)
        /// </summary>
        public bool AutoManageCooldowns { get { return SettingsFile.Instance.HandleCooldowns; } }

        /// <summary>
        /// GUI Setting for Automaticly handling Dragon soul Extra button clicks (On=True/Off=False)
        /// </summary>
        public bool AutoExtraActionButton { get { return SettingsFile.Instance.Handleextraactionbutton; } }

        /// <summary>
        /// GUI Setting for AOE (On = True/Off = False)
        /// </summary>
        public bool AutoAoe { get { return SettingsFile.Instance.shouldAOE; } }

        /// <summary>
        /// GUI Setting for MD Pet (On = True/Off = False)
        /// </summary>
        public bool AutoMDPet { get { return SettingsFile.Instance.shoulMDPet; } }

        /// <summary>
        /// GUI Setting for CPG (On = True/Off = False)
        /// </summary>
        public bool AutoCPG { get { return SettingsFile.Instance.shouldUseCPG; } }

        /// <summary>
        /// Write debug messages to the log
        /// </summary>
        private static bool debugOn;

        private RotationBase rotationBase;

        public override void OnButtonPress()
        {
            Configuration.Display(this);
        }

        public override bool WantButton
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// writes messages to the log file and the client UI
        /// </summary>
        /// <param name="msg">the message to write to the log</param>
        /// <param name="args">the arguments that accompany the message</param>
        public void Log(string msg, params object[] args)
        {
            // Stopwatch.GetTimestamp()
            if (msg != null)
            {
                Logging.Write(Color.Yellow, "[SPR] " + Version + ": " + msg, args);
            }
        }

        /// <summary>
        /// writes debug messages to the log file (false by default)
        /// </summary>
        /// <param name="msg">the message to write to the log</param>
        /// <param name="args">the arguments that accompany the message</param>
        public static void DebugLog(string msg, params object[] args)
        {
            if (msg != null && debugOn)
            {
                Logging.WriteDebug(string.Format("[SPR] " + Version + ": " + msg, args));
            }
        }

        /// <summary>
        /// Returns the string "Player" if the unit name is equal to our name.
        /// </summary>
        /// <param name="unit">the unit to check</param>
        /// <returns>a safe name for the log</returns>
        public static string SafeName(WoWUnit unit)
        {
            return (unit.Name == Me.Name) ? "Flynn Lives" : "some random!";
        }

        public override void Initialize()
        {
            ///////////////////////////////////////////////////////////////////
            //// PLEASE SET debugOn = true;
            //// TO UPLOAD YOUR LOG
            ///////////////////////////////////////////////////////////////////
            debugOn = false;

                foreach (WoWSpell racial in Spell.CurrentRacials)
                {
                    this.Log(" [Racial Abilitie] {0} ", racial.Name);
                }

            // If required HandleCombatLogEvent attach LUA events here
            // For debug purposes.
            // SPR.DebugLog("======================DUMPING SPELLS===========================");
            // Helpers.Spell.DumpSpells();
            // SPR.DebugLog("======================DUMPING AURAS===========================");
            // Helpers.Buff.DumpAuras();
            // SPR.DebugLog("===============================================================");
        }

        // public override void Pulse()
        // {
        // }

        /// <summary>
        /// Determine which group we are in if any.
        /// </summary>
        private string Group
        {
            get
            {
                if (Me.IsInParty)
                    return "PARTY";
                if (Me.IsInRaid)
                    return "RAID";
                return "SOLO";
            }
        }

        /// <summary>
        /// Depending on if we are grouped or solo, and if we are in a dungeon or battleground, return the rotation we will use.
        /// </summary>
        private Composite Rotation
        {
            get
            {
                Composite rotation;
                switch (Group)
                {
                    case "PARTY":
                    case "RAID":
                        rotation = Battlegrounds.IsInsideBattleground
                                       ? ActiveRotation.PVPRotation
                                       : ActiveRotation.PVERotation;
                        break;

                    default:
                        rotation = ActiveRotation.SingleRotation;
                        break;
                }

                return new Sequence(new DecoratorContinue(x => Me.CurrentTarget != null, rotation));
            }
        }

        /// <summary>
        /// Combat heal/buffs Behavior
        /// </summary>
        private Composite Medic
        {
            get
            {
                return ActiveRotation.Medic;
            }
        }

        /// <summary>
        /// Pre Combat buffs Behavior
        /// </summary>
        private Composite PreCombat
        {
            get
            {
                return ActiveRotation.PreCombat;
            }
        }

        /// <summary>
        ///  Setting the HB base behaviours.
        /// </summary>
        public override Composite PullBehavior
        {
            get
            {
                return Rotation;
            }
        }

        public override double? PullDistance
        {
            get
            {
                return ActiveRotation.CombatMaxDistance;
            }
        }

        public override Composite CombatBehavior
        {
            get
            {
                return Rotation;
            }
        }

        public override Composite CombatBuffBehavior
        {
            get
            {
                return this.Medic;
            }
        }

        public override Composite PreCombatBuffBehavior
        {
            get
            {
                return this.PreCombat;
            }
        }

        public override Composite RestBehavior
        {
            get
            {
                return this.PreCombat;
            }
        }

        /// <summary>
        /// If we havnt loaded a rotation for our character then do so by querying our character's class tree based on a Keyspell.
        /// </summary>
        private RotationBase ActiveRotation
        {
            get
            {
                if (this.rotationBase == null)
                {
                    QueryClassTree();
                }

                return this.rotationBase;
            }
        }

        /// <summary>This will: loop assemblies,
        /// loop types,
        /// filter types that are a subclass of RotationBase and not the abstract,
        /// create an instance of a RotationBase subclass so we can interigate KeySpell within the RotationBase subclass,
        /// Check if the character has the Keyspell,
        /// Set the active rotation to the matching RotationBase subclass.</summary>
        private void QueryClassTree()
        {
            var type = typeof (RotationBase);
            var types = AppDomain.CurrentDomain.GetAssemblies().ToList()
                .SelectMany(s => s.GetTypes())
                .Where(p => p.IsSubclassOf(type) && !p.IsAbstract);

            foreach (var x in types)
            {
                var constructorInfo = x.GetConstructor(new Type[] { });
                if (constructorInfo != null)
                {
                    var rb = constructorInfo.Invoke(new object[] { }) as RotationBase;
                    if (rb != null && SpellManager.HasSpell(rb.KeySpell))
                    {
                        DebugLog(" Using " + rb.Name + " rotation. Character has " + rb.KeySpell);
                        SetActiveRotation(rb);
                    }
                    else
                    {
                        if (rb != null)
                            DebugLog(" Skipping " + rb.Name + " rotation. Character is missing " + rb.KeySpell);
                    }
                }
            }
        }

        /// <summary>
        /// sets the character's active rotation.
        /// Print the help located in the specific rotation
        /// </summary>
        /// <param name="rb">an instance of the rotationbase</param>
        private void SetActiveRotation(RotationBase rb)
        {
            Log(" Greetings, level {0} user!", Me.Level);
            Log(" I am SPR.");
            Log(" I will create the perfect system for you.");
            Log(" I suggest we use the " + rb.Name + " rotation.");
            Log(" as I know you have " + rb.KeySpell);

            Logging.Write(Color.Red, " ========= ATTENTION USER ===================");
            Logging.Write(Color.Red, " If my internal algorithms are not computing");
            Logging.Write(Color.Red, " please contact my creator - wulf");
            Logging.Write(Color.Red, " Please be sure to attatch my log");
            Logging.Write(Color.Red, " as I record information to help resolve my issues.");
            Logging.Write(Color.Red, " NO LOG = NO SUPPORT");
            Logging.Write(Color.Red, " ========================================");

            Log(rb.Help);
            Log(" Let's execute the plan!");
            this.rotationBase = rb;
        }
    }
}
